@@ -0,0 +1,627 @@
<template>
  <div>
    <div class="preview" ref="huojian"></div>
  </div>
</template>

<script>
import * as THREE from "three";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader.js";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import { TransformControls } from "three/examples/jsm/controls/TransformControls.js";
import Stats from "three/examples/jsm/libs/stats.module";

import { onMounted, ref, toRaw } from "vue";

let camera = null;
let scene = null;
let renderer = null;
let orbitControls = null;
let loader = null;
let clock = null;
let stage = null;
let animationMixer = [];
let stats = null; //性能监控

export default {
  components: {},
  setup() {
    const huojian = ref("");
    const bufArrays = ref([]);
    const uuid = ref(null);
    const controlModel = ref([]);
    onMounted(() => {
      createScene();
      initThree();
      animate();
    });
    // 创建场景
    const createScene = () => {
      // 场景
      scene = new THREE.Scene();
      // 载入背景图
      // scene.background = new THREE.Color("#F1F3F5");
      // 使用画布来设置背景图
      scene.background = backgroundTexture();
      // 环境光
      const ambient = new THREE.AmbientLight(0xffffff, 1);
      scene.add(ambient);
      // 平行光
      const dLight = new THREE.DirectionalLight(0xffffff, 0.4);
      dLight.position.set(0, 500, 500);
      scene.add(dLight);
    };

    // 初始化3D场景
    const initThree = () => {
      // 缓存
      THREE.Cache.enabled = true;
      // 相机
      camera = new THREE.PerspectiveCamera(
        45,
        huojian.value.clientWidth / huojian.value.clientHeight,
        0.1,
        10000
      );
      camera.position.set(
        -3.169851723347616,
        594.2711691600982,
        424.01893674235856
      );
      // 让相机看向你创建的东西
      camera.lookAt(new THREE.Vector3(0, 0, 0));
      // 让相机正的看你创建出来的物体
      camera.up = new THREE.Vector3(0, 1, 0);
      // 3D渲染器
      renderer = new THREE.WebGLRenderer({
        antialias: true,
        alpha: true,
        logarithmicDepthBuffer: true,
        precision: "highp",
        powerPreference: "high-performance",
      });
      renderer.setSize(huojian.value.clientWidth, huojian.value.clientHeight);
      renderer.setPixelRatio(window.devicePixelRatio);
      renderer.outputEncoding = THREE.sRGBEncoding;
      renderer.shadowMap.enabled = true;
      renderer.shadowMap.type = THREE.VSMShadowMap;
      renderer.sortObjects = false;
      renderer.domElement.id = "content";
      const Empty = Object.freeze([]);
      // 全部裁剪
      renderer.clippingPlanes = Empty; // GUI sets it to globalPlanes
      // 是否可以裁剪
      renderer.localClippingEnabled = true;
      huojian.value.appendChild(renderer.domElement);
      // 添加监听方法
      renderer.domElement.addEventListener("click", objClick, false);
      // 相机控制器
      orbitControls = new OrbitControls(camera, renderer.domElement);
      // orbitControls.enablePan = false // 右键
      // orbitControls.enableZoom = false // 中键
      // orbitControls.enableRotate = false // 左键
      // orbitControls.minPolarAngle = 0

      // orbitControls.target = new THREE.Vector3(50, 50, 50);
      // 声明模型加载器
      loader = new GLTFLoader();
      const dracoLoader = new DRACOLoader();
      // 指定包含draco的WASM/JS解码库的文件夹路径
      dracoLoader.setDecoderPath("./3D/draco/");
      loader.setDRACOLoader(dracoLoader);
      // 添加监听方法
      window.addEventListener("resize", onResize, false);
      // 创建在地面的参照物是的线
      const gridHelper = new THREE.GridHelper(
        500,
        50,
        "rgb(200,2000,200)",
        "rgb(100,100,100)"
      );
      scene.add(gridHelper);
      // 创建性能监视器
      stats = Stats();
      const statsDom = stats.domElement;
      statsDom.style.position = "fixed";
      statsDom.style.top = "100px";
      statsDom.style.right = "5px";
      statsDom.style.left = "unset";
      huojian.value.appendChild(statsDom);
      // 三个辅助的坐标线
      const axesHelper = new THREE.AxesHelper(5);
      scene.add(axesHelper);
      // 模型控制器
      const transformControls = new TransformControls(
        camera,
        renderer.domElement
      );
      transformControls.size = 0.5;
      transformControls.name = "transformControls";
      // 模型控制器的一个使用
      transformControls.addEventListener("mouseDown", () => {
        orbitControls.enableRotate = false;
      });
      transformControls.addEventListener("mouseUp", () => {
        // 改
        orbitControls.enableRotate = true;
      });
      scene.add(transformControls);
      // 创建时间节点
      clock = new THREE.Clock();
      // 模型调用
      modelCreation();
    };

    // 创建场景中所需要的场景模型
    const modelCreation = () => {
      // 山谷模型的创建
      // loader.load("./3D/river/scene.gltf", (gitf) => {
      //   scene.add(gitf.scene);
      //   gitf.scene.name = "river";
      //   gitf.scene.position.set(0, 380, 0);
      //   gitf.scene.scale.set(8, 8, 8);
      // });
      // 地面
      stage = new THREE.Mesh(
        new THREE.BoxBufferGeometry(500, 30, 500),
        new THREE.MeshStandardMaterial({
          color: "rgb(105,105,105)",
          roughness: 0,
        })
      );
      stage.receiveShadow = true;
      stage.position.y = 10;
      stage.name = "dimian";
      scene.add(stage);
    };

    // 点击事件
    const objClick = (event) => {
      event.preventDefault();
      const intersects = getIntersects(event);
      if (intersects.length === 0) return;
      const model = intersects[0].object;
      const obj = toRaw(getTrueModel(model));
      // 给模型增加控制器
      glbChoosedFun(obj);
    };

    // 获取点击的最上级对象
    const getTrueModel = (obj) => {
      if (obj.parent && obj.parent.type !== "Scene") {
        return getTrueModel(obj.parent);
      }
      return obj;
    };

    // 获取鼠标与模型的交集
    const getIntersects = (event) => {
      // 计算位置点
      const mouse = new THREE.Vector2();
      mouse.x = (event.offsetX / huojian.value.clientWidth) * 2 - 1;
      mouse.y = -(event.offsetY / huojian.value.clientHeight) * 2 + 1;
      // 计算raycaster
      const raycaster = new THREE.Raycaster();
      raycaster.setFromCamera(mouse, camera);
      // 获取raycaster直线和所有模型相交的数组集合
      const intersects = raycaster.intersectObjects(controlModel.value);
      return intersects;
    };

    // 模型的控制器进行赋值
    const glbChoosedFun = (model) => {
      const transformControls = scene.children.find(
        (item) => item.name === "transformControls"
      );
      transformControls.detach();
      transformControls.attach(model);
      // 换位
      // translate    scale    rotate
      transformControls.setMode("translate");
    };

    // 自适应
    const onResize = () => {
      if (!camera) return;
      camera.aspect = huojian.value.clientWidth / huojian.value.clientHeight;
      camera.updateProjectionMatrix();
      renderer.setSize(huojian.value.clientWidth, huojian.value.clientHeight);
    };

    // 渲染场景
    const animate = () => {
      if (scene) {
        // 性能监视器
        stats.update();
        requestAnimationFrame(animate);
        renderer.render(scene, camera);
      }
    };

    // 画布创建背景
    const backgroundTexture = () => {
      const canvas = document.createElement("canvas");
      canvas.width = window.innerWidth;
      canvas.height = window.innerHeight;
      const ctx = canvas.getContext("2d");
      const gradient = ctx.createLinearGradient(0, 0, window.innerWidth, 0);
      gradient.addColorStop(0, "#4e22b7");
      gradient.addColorStop(1, "#FF6600");
      ctx.fillStyle = gradient;
      ctx.fillRect(0, 0, canvas.width, canvas.height);
      const canvasTexture = new THREE.CanvasTexture(canvas);
      return canvasTexture;
    };
    return {
      //  参数变量
      huojian,
      animationMixer,
      clock,
      bufArrays,
      uuid,
      // 方法
    };
  },
};
</script>

<style scoped lang="scss">
.preview {
  position: fixed;
  left: 0;
  width: 100%;
  height: 100%;
}
.huojian {
  position: fixed;
  width: 100px;
  height: 100px;
  background-color: red;
}
.switchs {
  list-style: none;
  position: absolute;
  top: 10px;
  left: 20px;
  width: 500px;
  height: 50px;
  li {
    margin-top: 10px;
  }
}
</style>

<style lang="scss">
.el-slider {
  width: 50% !important;
}
</style>
